package dfbz.com.dao.base;

import dfbz.com.annotation.Filed;
import dfbz.com.annotation.Table;
import dfbz.com.model.User;
import dfbz.com.model.UserInfo;
import dfbz.com.utils.JDBCUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 提供基本的增、删、改、查方法
 * <p>
 * 链接数据库，写sql，查询结果的封装，查询结果的返回
 * 通过注解  sql——哪张表--与对象类对应，表中哪个字段--对象中字段对应，设置哪个值--对象中的方法对应
 * 通过映射
 * <p>
 * 类的注解Table表明所查询列 表的名字
 * 字段的注解Filed表明 表中特殊字段（带_）与类中的字段对应（标准驼峰式）
 */

public class BaseDao<T> {

    /**
     * 查找所有对象
     *
     * @param aClass 类对象
     * @return 对象列表
     */
    public List<T> addAll(Class<User> aClass) {
        Table table = aClass.getAnnotation(Table.class);
        List<T> list = new ArrayList<>();
        if (table != null) {
            String tableName = table.value();
            //sql：select * from table_name
            String sql = "select*from " + tableName;

            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;


            try {
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(sql);
                resultSet = statement.executeQuery();

                //查到结果后将对象封装
                while (resultSet.next()) {
                    //得到这个类的所有字段，调用set的方法
                    Object obj = aClass.newInstance();
                    Field[] fields = aClass.getDeclaredFields();
                    for (Field field : fields) {
                        String fieldName = field.getName();

                        //表中特殊的字段名——为了拿到表中数据
                        if (field.isAnnotationPresent(Filed.class)) {
                            Filed anno = field.getAnnotation(Filed.class);
                            String fieldName2 = anno.value();
                            Object value2 = resultSet.getObject(fieldName2);
                            //得到和调用类中对应的set方法进行赋值——都是类中标准的命名方式——为了赋值封装对象
                            String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                            Method method = aClass.getDeclaredMethod(methodName, field.getType());
                            method.invoke(obj, value2);
                        } else {
                            Object value = resultSet.getObject(fieldName);
                            //得到和调用类中对应的set方法进行赋值——都是类中标准的命名方式——为了赋值封装对象
                            String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                            Method method = aClass.getDeclaredMethod(methodName, field.getType());
                            method.invoke(obj, value);
                        }

                    }
                    list.add((T) obj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }
        return list;

    }


    /**
     * 添加对象
     * <p>
     * 不用查表，没必要考虑字段名字的不同
     * 一个类要对应一个对象
     * 问题：对象遍历得到的字段值 怎样与表中设置的value(?,?,...)值位置对应?
     *
     * @param obj 前端传来的对象,传入的对象是object类型，不需要用泛型吗，object也可以得到具体的类型吗？
     */
    public void add(Object obj) {
        Class<?> aClass = obj.getClass();
        Table table = aClass.getDeclaredAnnotation(Table.class);
        if (table != null) {
            String tableName = table.value();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("insert into ").append(tableName).append(" values(");
            // String sql = "insert into "+tableName+" values(?,?,?,?) ";
            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;

            try {
                Field[] fields = aClass.getDeclaredFields();
                for (int i = 1; i <= fields.length; i++) {
                    if (i == fields.length) {
                        stringBuilder.append("?)");
                    } else {
                        stringBuilder.append("?,");
                    }
                }
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(stringBuilder.toString());
                int count = 0;

                for (Field field : fields) {
                    String fieldName = field.getName();
                    //获得get的方法
                    String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    Method method = aClass.getDeclaredMethod(methodName);

                    //执行方法
                    Object value = method.invoke(obj);

                    statement.setObject(++count, value);
                }

                statement.executeUpdate();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }

    }


    /**
     * 删除对象
     * <p>
     * 根据id查找对象，并将这个对象删除
     * 不需要对查找的对象进行值的改变
     *
     * @param id     前端传来的id值
     * @param aClass 类对象
     */

    public void delete(Object id, Class<T> aClass) {
        Table table = aClass.getDeclaredAnnotation(Table.class);

        if (table != null) {
            String tableName = table.value();
            //sql：delete from table_name where table_id = ...
            String sql = "delete from " + tableName + " where " + table.idName() + " = ? ";

            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;

            try {
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(sql);
                statement.setObject(1, id);
                statement.executeUpdate();

            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }

    }


    /**
     * 更新表中对象的数据
     * <p>
     * ?的值都是前端要设置的值
     * 泛型T，传入的对象是什么类型，会自动变成什么类型；如传入一个Student对象，则t就为Student类型；方便更新不同类型的对象
     * 这里的泛型T主要指一个对象
     *
     * @param t
     */
    public void update(T t) {

        Class<?> aClass = t.getClass();
        Table table = aClass.getDeclaredAnnotation(Table.class);
        if (table != null) {
            String tableName = table.value();

            StringBuilder sql = new StringBuilder();

            //sql: update table_name set table_id=?, ..=?, .....
            sql.append("update ").append(tableName).append(" set ").append(table.idName()).append(" = ?,");
            Field[] fields = aClass.getDeclaredFields();
            try {
                for (Field field : fields) {
                    String fieldName = field.getName();
                    Filed annoF = field.getAnnotation(Filed.class);
                    //如果不是有关id字段
                    if (!(field.getName().equals(table.idName())||(annoF != null && annoF.value().equals(table.idName())))) {
                        //获得除了getId之外的所有get方法
                        String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                        Method method = aClass.getDeclaredMethod(methodName);

                        //执行get方法___obj什么时候为null？？？
                        //如果一个对象中某个字段没有set一个值，则默认为null；即t对象中对应的字段为null时，通过get方法得到（返回值）就为null
                        //一般不会为null，前端如果没有设置值，得到的也只是个空字符串""

                        //拿到传入对象中封装的对应字段的值（get方法）
                        Object obj = method.invoke(t);
                        if (obj != null) {
                            if (annoF !=null){
                                sql.append(annoF.value()).append(" = ?,");
                            }else {
                                sql.append(fieldName).append(" = ?,");
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            //删除最后一个“，”
            sql.delete(sql.lastIndexOf(","), sql.length());
            sql.append(" where ").append(table.idName()).append(" = ?");

            //String sql = "update student set name = ?,phone = ?,address = ? where id = " + student.getId();

            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;

            try {
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(sql.toString());
                int count = 0;

                //table标注的得到id方法名
                Method getId = aClass.getDeclaredMethod(table.methodName());
                Object o = getId.invoke(t);
                //给第一个参数id=？赋值
                statement.setObject(++count, o);

                for (Field field : fields) {
                    String fieldName = field.getName();
                    Filed annoF = field.getAnnotation(Filed.class);
                    if (!(field.getName().equals(table.idName())||(annoF != null && annoF.value().equals(table.idName())))) {
                        //获得其他get的方法（除开getId的方法）
                        String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                        Method method = aClass.getDeclaredMethod(methodName);
                        Object obj = method.invoke(t);
                        if (obj != null) {
                            statement.setObject(++count, obj);
//                            System.out.println("----------");
//                            System.out.println("执行的get方法："+methodName);
//                            System.out.println("-------------------");
                        }
                    }
                }
                statement.setObject(++count, o);
                statement.execute();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }
    }


    /**
     * 通过id查询、封装并返回一个对象
     *
     * @param id
     * @param aClass 类对象
     * @return
     */

    public T get(Object id, Class<T> aClass) {
        Table table = aClass.getDeclaredAnnotation(Table.class);
        if (table != null) {

            String tableName = table.value();
            //sql：select * from table_name where id = ?
            String sql = "select * from " + tableName + " where id = ? ";

            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;
            T t = null;

            try {
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(sql);
                statement.setObject(1, id);
                resultSet = statement.executeQuery();


                if (resultSet.next()) {
                    Field[] fields = aClass.getDeclaredFields();
                    t = aClass.newInstance();
                    //表中特殊的字段名——为了拿到表中数据
                    for (Field field : fields) {
                        String fieldName = field.getName();
                        if (field.isAnnotationPresent(Filed.class)) {
                            Filed anno = field.getAnnotation(Filed.class);
                            String fieldName2 = anno.value();
                            Object value2 = resultSet.getObject(fieldName2);
                            //得到和调用类中对应的set方法进行赋值——都是类中标准的命名方式——为了赋值封装对象
                            String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                            Method method = aClass.getDeclaredMethod(methodName, field.getType());
                            method.invoke(t, value2);
                        } else {
                            Object value = resultSet.getObject(fieldName);
                            //得到和调用类中对应的set方法进行赋值——都是类中标准的命名方式——为了赋值封装对象
                            String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                            Method method = aClass.getDeclaredMethod(methodName, field.getType());
                            method.invoke(t, value);
                        }
                    }

                }
                return t;

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }
        return null;
    }

    /**
     * 多个删除
     *
     * @param ids
     * @param aClass
     */


    public void deleteAll(Object[] ids, Class<T> aClass) {
        Table table = aClass.getDeclaredAnnotation(Table.class);
        if (table != null) {
            String tableName = table.value();

            StringBuilder sql = new StringBuilder();
            //sql: delete from table_name where id = ?||id = ?....
            sql.append("delete from ").append(tableName).append(" where id = ?");
            for (int i = 1; i < ids.length; i++) {
                sql.append("||id = ?");
            }
            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;

            try {
                connection = JDBCUtil.getConnection();
                statement = connection.prepareStatement(sql.toString());
                statement.setObject(1, ids[0]);
                for (int i = 1; i < ids.length; i++) {

                    statement.setObject(i + 1, ids[i]);
                }

                statement.executeUpdate();

            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(resultSet, statement, connection);
            }
        }


    }

}